package org.adorsys.aderp.jpa.xls;

import java.beans.IntrospectionException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.adorsys.aderp.jpa.impl.AderpJPARepository;
import org.adorsys.aderp.jpa.impl.RepositoryMap;
import org.adorsys.xlseasy.annotation.SpreadsheetService;
import org.adorsys.xlseasy.boot.DateStylesInspector;
import org.adorsys.xlseasy.boot.ExcludedFieldInspector;
import org.adorsys.xlseasy.boot.KeyFieldInspector;
import org.adorsys.xlseasy.boot.SpreadSheetServiceBootStrap;
import org.adorsys.xlseasy.utils.XlseasyUtils;
import org.springframework.util.ReflectionUtils;

public abstract class DataImportExportServiceImpl<T> implements DataImportExportService<T> {
	
	private List<CollectionPropertyDescriptor> fieldDescriptors = 
			new ArrayList<CollectionPropertyDescriptor>();
	
	private DateStylesInspector dateStylesInspector;
	private KeyFieldInspector keyFieldInspector;
	private ExcludedFieldInspector excludedFieldInspector;
	
	private SpreadsheetService spreadsheetService;
	/**
	 * Invoke this method in the post construct of the bean.
	 */
	public void fromPostConstruct(){
		Class<T> dataClass = dataClass();
		List<Field> workbookFields = XlseasyUtils.readWorkbookFields(dataClass);
		for (Field field : workbookFields) {
			Class<?> elementType = XlseasyUtils.extractElementType(field);
			if(elementType==null) continue;

			try {
				fieldDescriptors.add(new CollectionPropertyDescriptor(field.getName(), dataClass, elementType));
			} catch (IntrospectionException e) {
				throw new IllegalStateException(e);
			}
			
			SpreadSheetServiceBootStrap bootStrap = new SpreadSheetServiceBootStrap();
			bootStrap.setWorkbookKlass(dataClass);
			
			if(dateStylesInspector==null) dateStylesInspector = new AderpJpaDateStylesInspector(dataClass);
			bootStrap.setDateStylesInspector(dateStylesInspector);
			
			if(keyFieldInspector==null) keyFieldInspector=new AderpJpaKeyFieldInspector(dataClass, getRepositoryMap());
			bootStrap.setKeyFieldInspector(keyFieldInspector);
			
			if(excludedFieldInspector==null)excludedFieldInspector=new AderpJpaExcludedFieldInspector();
			bootStrap.setExcludedFieldInspector(excludedFieldInspector);
			
			spreadsheetService = bootStrap.createSpreadService();
		}
		
	}
	
	@Override
	public void importDataXLS(InputStream xlsStream) {
		T data = spreadsheetService.loadSpreadsheet(xlsStream, dataClass());
		importData(data);
	}

	@SuppressWarnings("unchecked")
	@Override
	public void importData(T data) {
		for (CollectionPropertyDescriptor p : fieldDescriptors) {
			Class<?> elementType = p.getElementType();
			Collection<?> result = (Collection<?>) ReflectionUtils.invokeMethod(p.getReadMethod(), data);
			@SuppressWarnings("rawtypes")
			AderpJPARepository aderpJPARepository = getRepositoryMap().getRepository(elementType);
			for (Object entity : result) {
				aderpJPARepository.save(entity);
			}
		}
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public T exportData() {
		T data = newInstance();
		for (CollectionPropertyDescriptor p : fieldDescriptors) {
			Class<?> elementType = p.getElementType();
			AderpJPARepository aderpJPARepository = getRepositoryMap().getRepository(elementType);
			List allEntities = aderpJPARepository.findAll();
			Collection<?> result = (Collection<?>) ReflectionUtils.invokeMethod(p.getReadMethod(), data);
			result.addAll(allEntities);
		}
		return data;
	}

	@Override
	public StreamingOutput exportDataXLS() {
		final T data = exportData();
		return new StreamingOutput()
		{
			@Override
			public void write(OutputStream outputStream) throws IOException
			{
				spreadsheetService.saveSpreadsheet(dataClass(), data,
						outputStream);
			}
		};
	}
	
	public abstract Class<T> dataClass();
	public abstract T newInstance();
	public abstract RepositoryMap getRepositoryMap();
}
